namespace EyeSoft.Wpf.Facilities
{
	using System;
	using System.Collections.Generic;
	using System.Diagnostics;

	internal class PropertiesValues
	{
		private readonly Action<string> onPropertyChanging;

		private readonly Action<string> onPropertyChanged;

		private readonly IDictionary<string, object> fields = new Dictionary<string, object>();

		public PropertiesValues(Action<string> onPropertyChanging, Action<string> onPropertyChanged)
		{
			this.onPropertyChanging = onPropertyChanging;
			this.onPropertyChanged = onPropertyChanged;
		}

		public int Changes { get; private set; }

		public object GetValue(string propertyName)
		{
			if (!fields.ContainsKey(propertyName))
			{
				return null;
			}

			return fields[propertyName];
		}

		public T GetValue<T>(string propertyName)
		{
			if (!fields.ContainsKey(propertyName))
			{
				return default(T);
			}

			return (T)fields[propertyName];
		}

		public T GetProperty<T>()
		{
			var propertyName = GetPropertyName();

			return GetValue<T>(propertyName);
		}

		public void SetProperty<T>(T value, bool propertyChangedSuspended)
		{
			var propertyName = GetPropertyName();

			var original = default(T);

			if (fields.ContainsKey(propertyName))
			{
				original = (T)fields[propertyName];
			}

			if (ReferenceEquals(value, original))
			{
				return;
			}

			if (typeof(T).IsPrimitiveType() && EqualityComparer<T>.Default.Equals(value, original))
			{
				return;
			}

			onPropertyChanging(propertyName);

			fields[propertyName] = value;

			if (!propertyChangedSuspended)
			{
				Changes++;
			}

			onPropertyChanged(propertyName);
		}

		[DebuggerStepThrough]
		private string GetPropertyName()
		{
			var callStackTrace = new StackTrace();
			var propertyFrame = callStackTrace.GetFrame(3);
			var properyAccessorName = propertyFrame.GetMethod().Name;

			var propertyName =
				properyAccessorName
					.Replace("get_", null)
					.Replace("set_", null);

			return propertyName;
		}
	}
}